package cn.xutongxue.gtms.utils.excel;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@SuppressWarnings("unchecked")
public class ExportListener<T> {

    /**
     * 实体类队形的Service方法
     */
    private final IService<T> baseService;

    public ExportListener(IService<T> baseService) {
        this.baseService = baseService;
    }

    private static final String DATA_FORMAT = "yyyy-MM-dd-HH-mm-ss";

    private static final String CHARACTER_UTF_8 = "UTF-8";

    private static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

    private static final String CONTENT_DISPOSITION = "Content-Disposition";

    private static final String ACCESS_CONTROL_EXPOSE = "Access-Control-Expose-Headers";

    private static final int PAGE_SIZE = 10000;

    /**
     * 导出Excel二进制流
     *
     * @param response
     * @param sheetName    sheet名称
     * @param pojoClass    导出Excel对象字节码
     * @param queryWrapper 查询条件
     * @throws IOException
     */
    public void exportExcel(HttpServletResponse response, String sheetName, Class<T> pojoClass,
                            LambdaQueryWrapper<T> queryWrapper) throws IOException {
        ServletOutputStream out = getServletOutputStream(response, sheetName);
        // 这里 需要指定写用哪个class去写
        ExcelWriter excelWriter = EasyExcel.write(out, pojoClass).build();
        // 这里注意 如果同一个sheet只要创建一次
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
        int totalCount = Math.toIntExact(baseService.count(queryWrapper));
        int pageNumber = (int) Math.ceil((double) totalCount / (double) PAGE_SIZE);    // 分页条数看情况

        // 去调用写入,根据数据库分页的总的页数来
        for (int i = 1; i <= pageNumber; i++) {
            // 先定义一个空集合每次循环使他变成null减少内存的占用
            List<T> recordList = new ArrayList<>();
            Page<T> page = new Page<>(i, PAGE_SIZE);
            Page<T> pojoIPage = baseService.page(page, queryWrapper);
            T target = null;
            for (T tmp : pojoIPage.getRecords()) {
                try {
                    target = pojoClass.newInstance();
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
                BeanUtil.copyProperties(tmp, target);
                recordList.add(target);
            }
            // recordList = pojoIPage.getRecords();
            excelWriter.write(recordList, writeSheet);
            recordList.clear();
        }
        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
        out.flush();
    }

    /**
     * 查询分页优化的方法
     */
    public void exportNoQueryCount(HttpServletResponse response, String sheetName, Class<T> pojoClass,
                                   LambdaQueryWrapper<T> queryWrapper) throws IOException {
        ServletOutputStream out = getServletOutputStream(response, sheetName);
        ExcelWriter excelWriter = EasyExcel.write(out, pojoClass).build();

        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
        int startIndex = 1;
        while (true) {
            int startParam = (startIndex - 1) * PAGE_SIZE;
            int pageNumber = (int) Math.ceil((double) startParam / (double) PAGE_SIZE + 1);
            Page<T> page = new Page<>(pageNumber, PAGE_SIZE, false);
            Page<T> pojoIPage = baseService.page(page, queryWrapper);
            List<T> recordList = pojoIPage.getRecords();
            if (CollectionUtils.isEmpty(recordList)) {
                break;
            }
            startIndex++;
            excelWriter.write(recordList, writeSheet);
        }
        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
    }

    public static ServletOutputStream getServletOutputStream(HttpServletResponse response, String sheetName) throws IOException {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATA_FORMAT);
        String nowTime = formatter.format(LocalDateTime.now());
        String fileName = sheetName.concat("_").concat(nowTime).concat(".xlsx");
        response.setContentType(CONTENT_TYPE);
        // 设置字符集为utf-8
        response.setCharacterEncoding(CHARACTER_UTF_8);

        // 用postman测正常，浏览器多了filename_=utf-8等字样
        // response.setHeader(CONTENT_DISPOSITION,
        //         "attachment;filename=" + URLEncoder.encode(fileName, CHARACTER_UTF_8)
        //                 + ";filename*=utf-8''" + URLEncoder.encode(fileName, CHARACTER_UTF_8));

        response.setHeader(ACCESS_CONTROL_EXPOSE, CONTENT_DISPOSITION); // 使前端可以获取文件名解码转成中文
        // postman测会乱码，但浏览器下载就正常,真实开发是用这个，前端需要解码，文件名才能看到中文
        response.setHeader(CONTENT_DISPOSITION,
                "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        return response.getOutputStream();
    }
}

